

const data = {
  name: 'charlie',
  age: 18,
  // 在 vue 里一进来就会把所有属性都建立监听
  // 但如果data里面 有对象、数组的数据的话就又都会深度监听一遍（递归），会很卡，造成性能消耗
  // 在 vue3 中 使用 proxy ，会在使用到对应的数据的时候才会去监听
  friend: {
    friendName: 'tom'
  },
  course: ['Chinese', 'English', 'Math', 'Physics']
}

// 保存数组原型上的方法
const oldArrProto = Array.prototype
// 把 oldArrProto的原型上的方法 提供给 新创建的对象
const newArrProto = Object.create(oldArrProto)
// 定义一个方法名数组并循环添加到 newArrProto上 
const arrName = ['push', 'pop', 'shift', 'unshift', 'splice']
arrName.forEach(methodName => {
  // 从newArrProto里继承过来的原型上 把数组方法 添加给对应的数组名
  newArrProto[methodName] = function () {
    console.log('数组的值更新视图')
    oldArrProto[methodName].call(this, ...arguments)
  }
})
// 现在要是再使用 .push()等 就能 1. 监听，更新视图 2. 原本的功能


observer(data)

function observer(target) { // 变成响应式数据
  // 如果传进来的不是一个对象或没有数据 则直接返回
  if (typeof target !== 'object' || target === null) return target

  if (Array.isArray(target)) {
    // 判断是否为数组 把数组之前的方法重新写一遍
    target.__proto__ = newArrProto
  }

  // 循环监听每一个值
  for (let key in target) {
    if(typeof target[key] === 'object' ) { // 判断是否为对象 是的话就再返回去监听一遍
      observer(target[key])
    } else {
      defineReactive(target, key, target[key])
    }
  }
}

function defineReactive(target, key, value) { // 监听单个值
  Object.defineProperty(target, key, {
    get() {
      return value
    },
    set(newVal) {
      observer(newVal) // 深度监听（递归）
      if (newVal !== value) {
        value = newVal
        console.log('更新了视图---' + value)
      }
    }
  })
}

data.name = 'jack'

data.friend.friendName = 'amy'

data.age = { number: 20 }
data.age.number = 22

// Object.defineProperty 无法监听到 删除、添加
delete age // ==> Vue.delete
data.group = '高三' // ==> Vue.set

// 直接用下标可以改变值
data.course[0] = 'PE'
// 但数组方法 监听不了 要重写一下数组方法
console.log(data.course.push)
data.course.push('biology')

